package com.sobot.finance.poc.web.util;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpStatus;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.sobot.finance.poc.web.config.SysConfig;
import com.sobot.finance.poc.web.cons.SysConst;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * API
 *
 * @author baiyp
 */
@Slf4j
@Component
public class ApiClient {

    @Resource
    private SysConfig sysConfig;

    @Resource
    private RedisClient redisClient;

    /**
     * 线程安全
     */
    protected static Gson gson = new Gson();

    /**
     * 默认类型
     */
    private static final String CONTENT_TYPE = "application/json;charset=utf-8";

    /**
     * 超时时间：毫秒
     */
    private static final int TIME_OUT = 60_000;


    public String createTask(Object param) {
        log.info("[创建任务-开始]: {}", gson.toJson(param));
        String taskId = null;
        try {
            String url = StrUtil.format(sysConfig.getCreateTaskUrl(), sysConfig.getCompanyId());
            String token = getToken();
            // 请求
            String result = HttpRequest.post(url)
                    .header(Header.CONTENT_TYPE, CONTENT_TYPE)
                    .header(Header.AUTHORIZATION, token)
                    .body(gson.toJson(param))
                    .timeout(TIME_OUT)
                    .execute()
                    .body();

            log.info("[result]: {}", gson.toJson(result));

            ApiRes<TaskContent> res = gson.fromJson(result, new TypeToken<ApiRes<TaskContent>>() {
            }.getType());

            if (Objects.nonNull(res) && res.isSuccess()) {
                taskId = res.getContent().getTaskId();
            }
        } catch (Exception e) {
            log.error("[异常]: ", e);
        }
        log.info("[创建任务-结束]: {}", gson.toJson(taskId));
        return taskId;
    }

    public boolean uploadTel(String taskId, Object param) {
        log.info("[上传拨打数据-开始]: {}", gson.toJson(param));
        boolean success = false;
        try {
            String url = StrUtil.format(sysConfig.getUploadTelUrl(), sysConfig.getCompanyId(), taskId);
            String token = getToken();
            // 请求
            String result = HttpRequest.post(url)
                    .header(Header.CONTENT_TYPE, CONTENT_TYPE)
                    .header(Header.AUTHORIZATION, token)
                    .body(gson.toJson(param))
                    .timeout(TIME_OUT)
                    .execute()
                    .body();

            log.info("[result]: {}", gson.toJson(result));

            ApiRes<String> res = gson.fromJson(result, new TypeToken<ApiRes<String>>() {
            }.getType());

            if (Objects.nonNull(res) && res.isSuccess()) {
                success = true;
            }
        } catch (Exception e) {
            log.error("[异常]: ", e);
        }
        log.info("[上传拨打数据-结束]: {}", gson.toJson(success));
        return success;
    }

    private TokenContent getToken(String apiKey, String apiSecret, String tokenUrl) {
        TokenContent content = null;
        log.info("[请求token-开始]");
        try {
            String url = StrUtil.format(tokenUrl, apiKey, apiSecret);
            // 请求
            String result = HttpRequest.get(url)
                    .header(Header.CONTENT_TYPE, CONTENT_TYPE)
                    .timeout(TIME_OUT)
                    .execute()
                    .body();

            log.info("[result]: {}", gson.toJson(result));

            ApiRes<TokenContent> res = gson.fromJson(result, new TypeToken<ApiRes<TokenContent>>() {
            }.getType());

            if (Objects.nonNull(res) && res.isSuccess()) {
                content = res.getContent();
            }
        } catch (Exception e) {
            log.error("[异常]: ", e);
        }
        log.info("[请求token-结束]: {}", gson.toJson(content));
        return content;
    }

    private String getToken() {
        String token = redisClient.get(SysConst.TOKEN_KEY_TMP);
        if (StrUtil.isBlank(token)) {
            String apiKey = sysConfig.getApiKey();
            String apiSecret = sysConfig.getApiSecret();
            String tokenUrl = sysConfig.getTokenUrl();
            TokenContent content = getToken(apiKey, apiSecret, tokenUrl);
            if (Objects.nonNull(content)) {
                Map<Object, Object> tmpMap = MapUtil.builder()
                        .put("tokenType", content.getTokenType())
                        .put("accessToken", content.getAccessToken())
                        .build();
                token = StrUtil.format(SysConst.TOKEN_VAL_TMP, tmpMap);
                int expiresIn = content.getExpiresIn();
                long createTime = content.getCreateTime();
                long expiresTime = createTime + expiresIn * 1_000;
                long timeout = expiresTime - System.currentTimeMillis();
                redisClient.set(SysConst.TOKEN_KEY_TMP, token, timeout, TimeUnit.MILLISECONDS);
            }
        }
        log.info("[token]: {}", token);
        return token;
    }

    /**
     * @author baiyp
     */
    @Data
    public static class ApiRes<T> {
        private Integer code;
        private String message;
        private T content;

        public boolean isSuccess() {
            return Objects.nonNull(code) && code == HttpStatus.HTTP_OK;
        }

        public static ApiRes succeed() {
            ApiRes res = new ApiRes();
            res.setCode(HttpStatus.HTTP_OK);
            res.setMessage("成功");
            return res;
        }
    }

    /**
     * @author baiyp
     */
    @Data
    public static class TokenContent {
        private String accessToken;
        private String tokenType;
        // 单位：秒
        private Integer expiresIn;
        // 单位毫秒
        private Long createTime;
    }

    /**
     * @author baiyp
     */
    @Data
    public static class TaskContent {
        private String taskId;
        private String taskName;
        private String templateId;
        private String templateName;
        private String companyId;
    }
}
